#ifndef Define_LcGraphCut
#define Define_LcGraphCut

#include <cstdio>
#include <cstdlib>
#include <iostream>

#include <cstring>
#include <opencv2/opencv.hpp>
#include <opencv2/flann/config.h>

#include <opencv2/legacy/legacy.hpp>		// EM
#include <opencv2/contrib/contrib.hpp>		// colormap
#include <opencv2/nonfree/nonfree.hpp>		// SIFT


#include "LcBasic.h"

#include "Classifier.h"

#include "SLIC.h"

using namespace std;
using namespace cv;

void superResReconstruct(Mat & res, Mat & label_map, Mat & dst);

class NodeStatistic
{
public:
	LcColorMoment color_moment;
	float n ;
	float sum_likelihood;

	float sum_position_prior;

	NodeStatistic();

	void addPoint(Vec3b & color, float likelihood);
};

class EdgeStatistic
{
public:
	float len;
	float sum_Pb;
	EdgeStatistic();
};

class NodeFeatureExtractor
{
public:
	int dim;
	virtual void compute( NodeStatistic & node, Mat & desc){;}
};

class EdgeFeatureExtractor
{
public:
	int dim;
	virtual void compute(	NodeStatistic & node1,
							NodeStatistic & node2,
							EdgeStatistic & edge,
							Mat & desc){;}
};


//================================

#include "graph.h"

class LcMRF
{
public:

	vector< Mat > lambda;

	bool IF_NODE_BIAS;

	float node_bias;

	bool IF_EDGE_BIAS;

	float edge_bias;

	LcMRF();

	void set( vector< int > dims);

	LcValidator predict(	Mat & node_desc, 
							Mat & edge_desc,
							Mat & edge_map,
							Mat & res,
							Mat & label);

	void setParameter( Mat & para );

	void getParameter( Mat & para );

};

class LcLMOpt
{
public:

	LcLMOpt();

	LcLMOpt( 	vector<Mat> & n_node_descs,
				vector<Mat> & n_edge_descs,
				vector<Mat> & n_edge_maps,	
				vector<Mat> & n_labels);

	void setMRF( LcMRF * n_MRF ){
		my_MRF = n_MRF;
		my_MRF->getParameter(my_parameter);}


	LcMRF * my_MRF;

	Mat my_parameter;

	vector<Mat> node_descs;
	vector<Mat> edge_descs;
	vector<Mat> edge_maps;	
	vector<Mat> labels;

	void set(	vector<Mat> & n_node_descs,
				vector<Mat> & n_edge_descs,
				vector<Mat> & n_edge_maps,	
				vector<Mat> & n_labels);

	int n_frame;

	int jac_down_rate;

	float calcScore( int down_rate , Mat & para, int start = -1);

	void calcJacobian( Mat & jac);
	void calcErr( Mat & err );

	void work();

	double learning_rate;
	double cooling_rate;
	int iter_times;

};



//===================================

class LcGraphCut
{
public:

	LcGraphCut();

	void set( Mat & frm, Mat & likelihood );

	int visual;	

	double compact_parameter;
	int n_superpixel;

	int expand_times;

	vector< NodeStatistic > node_statistic;

	vector<vector< EdgeStatistic*> > edge_statistic;

	void collectNodeStatistic( Mat & frm, Mat & likelihood); // output to node_statistic

	void collectHeatmap(Mat & likelihood);

	void collectPb( Mat & Pb_result );

	void getLabel( Mat & gt, Mat & label);

	Mat getBinayScoreMat( Mat & label_map, Mat & adj_mat , EdgeFeatureExtractor * computer);

	void visualLinkAmplitude( Mat & label_map, Mat & adj_mat , Mat & score_mat, Mat * source = NULL);
	//should after collect

	void output(string prefix);

	void computeAllNode( Mat & desc);
	void computeAllEdge( Mat & edge_map, Mat & desc );

	vector<int> filter_map;
	void balanceBackGround(Mat & likelihood);	

	Mat label_map;	

private:
	vector< NodeFeatureExtractor* > node_computer;
	void setNodeComputer();

	int dim_node_feature;
	int getDimNode();

	vector< EdgeFeatureExtractor* > edge_computer;
	void setEdgeComputer();

	int dim_edge_feature;
	int getDimEdge();

	int n_node;

	static void mergeLikeli( Mat & frm, Mat & likelihood, char * ch, Mat & dst);	

	void getSLIC( Mat & src, Mat & label_map );

	Mat adj_mat;//adjacency matrix  
	static Mat getAdjancencyMatrix( Mat & label_map, int n_node);
	static void expandAdjancencyMatrix( Mat & adj_mat , int expand_times);

	void visualLink( Mat & label_map, Mat & adj_mat ,int n_node);

	vector<Point2f> getNodeCenter(Mat & label_map , int n_node);

	Mat showSLIC( Mat & src, Mat & label_map );

	char channel_code[3];
};

//======================================================
//==========NodeFeatureExtractor Family=================

class SpMeanLikeli : public NodeFeatureExtractor
{
public:
	SpMeanLikeli(){ dim = 1; };

	void compute( NodeStatistic & node, Mat & desc);

};


class SpPositionPrior : public NodeFeatureExtractor
{
public:
	SpPositionPrior(){ dim = 1; };

	void compute( NodeStatistic & node, Mat & desc);

};


class SpColorMoment : public NodeFeatureExtractor
{
public:
	SpColorMoment(){ dim = 9; };

	void compute( NodeStatistic & node, Mat & desc);

};

//======================================================
//==========EdgeFeatureExtractor Family=================

class SpColorDiff : public EdgeFeatureExtractor
{
public:
	SpColorDiff(){ dim = 3; };

	void compute(	NodeStatistic & node1,
					NodeStatistic & node2,
					EdgeStatistic & edge,
					Mat & desc);


};

class SpPb : public EdgeFeatureExtractor
{
public:
	SpPb(){ dim = 1;}

	void compute(	NodeStatistic & node1,
					NodeStatistic & node2,
					EdgeStatistic & edge,
					Mat & desc);
};

class SpGaussTest : public EdgeFeatureExtractor
{
public:
	SpGaussTest(){ dim = 1; };

	void compute(	NodeStatistic & node1,
					NodeStatistic & node2,
					EdgeStatistic & edge,
					Mat & desc);	
};

class SpIndTTest : public EdgeFeatureExtractor
{
public:
	SpIndTTest(){ dim = 3; };

	void compute(	NodeStatistic & node1,
					NodeStatistic & node2,
					EdgeStatistic & edge,
					Mat & desc);	
};


#endif
